from collections.abc import Sequence
from typing import Any

from sentry.issues.grouptype import GroupCategory
from sentry.models.organization import Organization
from sentry.snuba.dataset import Dataset
from sentry.snuba.events import Columns

"""
Issue category specific components to computing a preview for a set of rules.

To add support for a new issue category/dataset:
    1. Update get_dataset_from_category
    2. Add mapping from Dataset to snuba column name
        a. The column name should be a field in sentry.snuba.events.Column
    3. Add category-specific query params for get_update_kwargs_for_groups and get_update_kwargs_for_group
"""


def get_dataset_from_category(category: int, organization: Organization) -> Dataset:
    if category == GroupCategory.ERROR.value:
        return Dataset.Events
    return Dataset.IssuePlatform


# Maps datasets to snuba column name
DATASET_TO_COLUMN_NAME: dict[Dataset, str] = {
    Dataset.Events: "event_name",
    Dataset.Transactions: "transaction_name",
    Dataset.IssuePlatform: "issue_platform_name",
}


# Given a list of Column Enum objects, return their actual column name in each dataset that is supported
def get_dataset_columns(columns: Sequence[Columns]) -> dict[Dataset, Sequence[str]]:
    dataset_columns: dict[Dataset, Sequence[str]] = {}
    for dataset, column_name in DATASET_TO_COLUMN_NAME.items():
        dataset_columns[dataset] = [
            getattr(column.value, column_name)
            for column in columns
            if getattr(column.value, column_name) is not None
        ]

    return dataset_columns


def _events_from_groups_kwargs(
    group_ids: Sequence[int], kwargs: dict[str, Any], has_issue_state_condition: bool = True
) -> dict[str, Any]:
    if has_issue_state_condition:
        kwargs["conditions"] = [("group_id", "IN", group_ids)]
    return kwargs


def _transactions_from_groups_kwargs(
    group_ids: Sequence[int], kwargs: dict[str, Any], has_issue_state_condition: bool = True
) -> dict[str, Any]:
    if has_issue_state_condition:
        kwargs["having"] = [("group_id", "IN", group_ids)]
        kwargs["conditions"] = [[["hasAny", ["group_ids", ["array", group_ids]]], "=", 1]]
    if "aggregations" not in kwargs:
        kwargs["aggregations"] = []
    kwargs["aggregations"].append(("arrayJoin", ["group_ids"], "group_id"))
    return kwargs


"""
Returns the rows that contain the group id.
If there's a many-to-many relationship, the group id column should be arrayjoined.
If there are no issue state changes (causes no group ids), then do not filter by group ids.
"""


def get_update_kwargs_for_groups(
    dataset: Dataset | None,
    group_ids: Sequence[int],
    kwargs: dict[str, Any],
    has_issue_state_condition: bool = True,
) -> dict[str, Any]:
    if dataset == Dataset.Transactions:
        return _transactions_from_groups_kwargs(group_ids, kwargs, has_issue_state_condition)
    return _events_from_groups_kwargs(group_ids, kwargs, has_issue_state_condition)


def _events_from_group_kwargs(group_id: int, kwargs: dict[str, Any]) -> dict[str, Any]:
    kwargs["conditions"] = [("group_id", "=", group_id)]
    return kwargs


def _transactions_from_group_kwargs(group_id: int, kwargs: dict[str, Any]) -> dict[str, Any]:
    kwargs["conditions"] = [[["has", ["group_ids", group_id]], "=", 1]]
    return kwargs


"""
Returns the rows that reference the group id without arrayjoining.
"""


def get_update_kwargs_for_group(
    dataset: Dataset, group_id: int, kwargs: dict[str, Any]
) -> dict[str, Any]:
    if dataset == Dataset.Transactions:
        return _transactions_from_group_kwargs(group_id, kwargs)
    return _events_from_group_kwargs(group_id, kwargs)
